#!/usr/bin/env python3
"""
Demo Server with intentional connection failure for testing
"""

import asyncio
import json
import time
import uuid
from datetime import datetime
from typing import Dict, List, Optional
import websockets
from websockets.server import serve
import logging

# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# In-memory storage
users: Dict[str, dict] = {}
contracts: Dict[str, dict] = {}
prices: Dict[str, dict] = {}  # symbol -> latest price data
connected_clients: set = set()

# Configuration
SUPPORTED_SYMBOLS = ["ETHUSDT", "BTCUSDT"]
INITIAL_BALANCE = 10000.0
PROFIT_RATES = {
    "10m": 0.8,
    "30m": 0.85,
    "1h": 0.85,
    "1d": 0.85
}
PERIOD_SECONDS = {
    "10m": 600,
    "30m": 1800,
    "1h": 3600,
    "1d": 86400
}

async def broadcast_price_update(price_data: dict):
    """Broadcast price update to all connected clients"""
    global connected_clients
    if connected_clients:
        message = json.dumps({
            "type": "price_update",
            "data": price_data
        })
        
        disconnected = set()
        for client in connected_clients.copy():
            try:
                await client.send(message)
            except websockets.exceptions.ConnectionClosed:
                disconnected.add(client)
        
        # Clean up disconnected clients
        connected_clients -= disconnected

async def connect_to_binance_websocket(symbol: str):
    """Connect to Binance WebSocket for real price data - INTENTIONALLY FAIL"""
    # 使用错误的URL来模拟连接失败
    url = f"wss://invalid-url-that-will-fail.com/{symbol}@index"
    connection_attempts = 0
    max_attempts = 3
    
    while connection_attempts < max_attempts:
        try:
            logger.info(f"连接到币安WebSocket: {symbol} (尝试 {connection_attempts + 1}/{max_attempts})")
            
            # 这里会失败，因为URL是无效的
            async with websockets.connect(url, ping_interval=300, ping_timeout=900) as websocket:
                logger.info(f"✅ {symbol} WebSocket连接成功")
                connection_attempts = 0
                
                async for message in websocket:
                    try:
                        data = json.loads(message)
                        if data.get("e") == "index" and data.get("s") == symbol:
                            prices[symbol] = data
                            await broadcast_price_update(data)
                            logger.info(f"📈 {symbol} 真实价格: {data['p']} (clients: {len(connected_clients)})")
                    except json.JSONDecodeError as e:
                        logger.error(f"❌ {symbol} 消息解析失败: {e}")
                    except Exception as e:
                        logger.error(f"❌ {symbol} 处理消息错误: {e}")
                        
        except Exception as e:
            connection_attempts += 1
            logger.error(f"❌ {symbol} WebSocket连接错误: {e}")
            if connection_attempts < max_attempts:
                logger.info(f"🔄 {symbol} 5秒后重连...")
                await asyncio.sleep(5)
            else:
                logger.error(f"❌ {symbol} 连接失败，价格将显示为0")
                # 设置价格为0表示连接失败
                price_data = {
                    "e": "index",
                    "E": int(time.time() * 1000),
                    "s": symbol,
                    "p": "0"
                }
                prices[symbol] = price_data
                await broadcast_price_update(price_data)
                logger.info(f"🔴 {symbol} 价格设置为0 (连接失败)")
                break

async def start_price_feeds():
    """Start real-time price feeds from Binance"""
    tasks = []
    for symbol in SUPPORTED_SYMBOLS:
        task = asyncio.create_task(connect_to_binance_websocket(symbol))
        tasks.append(task)
    
    # 等待所有WebSocket连接
    await asyncio.gather(*tasks, return_exceptions=True)

async def check_expired_contracts():
    """Check and settle expired contracts"""
    while True:
        current_time = int(time.time() * 1000)
        
        for contract_id, contract in contracts.items():
            if contract["status"] == "active" and contract["expiry_timestamp"] <= current_time:
                await settle_contract(contract_id)
        
        await asyncio.sleep(5)  # Check every 5 seconds

async def settle_contract(contract_id: str):
    """Settle an expired contract"""
    contract = contracts.get(contract_id)
    if not contract or contract["status"] != "active":
        return
    
    symbol = contract["symbol"]
    current_price_data = prices.get(symbol)
    
    if not current_price_data or float(current_price_data["p"]) == 0:
        # Mark as unknown and refund
        contract["status"] = "unknown"
        user = users[contract["user_id"]]
        user["balance"] += contract["bet_amount"]
        logger.warning(f"Contract {contract_id} marked as unknown - price unavailable")
        return
    
    final_price = float(current_price_data["p"])
    contract["final_price"] = final_price
    
    # Determine if prediction was correct
    price_increased = final_price > contract["anchor_price"]
    prediction_correct = (
        (contract["direction"] == "up" and price_increased) or
        (contract["direction"] == "down" and not price_increased)
    )
    
    user = users[contract["user_id"]]
    
    if prediction_correct:
        # Calculate profit
        profit_rate = PROFIT_RATES[contract["period"]]
        profit = contract["bet_amount"] * profit_rate
        contract["profit"] = profit
        
        # Return bet amount + profit
        user["balance"] += contract["bet_amount"] + profit
        logger.info(f"Contract {contract_id} won: profit {profit} USDT")
    else:
        # Loss - no return (bet amount already deducted)
        contract["profit"] = -contract["bet_amount"]
        logger.info(f"Contract {contract_id} lost: {contract['bet_amount']} USDT")
    
    contract["status"] = "settled"
    contract["settled_at"] = datetime.now().isoformat()

async def handle_client(websocket, path):
    """Handle WebSocket client connections"""
    connected_clients.add(websocket)
    logger.info(f"Client connected. Total clients: {len(connected_clients)}")
    
    try:
        async for message in websocket:
            try:
                data = json.loads(message)
                response = await handle_message(data)
                await websocket.send(json.dumps(response))
            except Exception as e:
                error_response = {"error": str(e)}
                await websocket.send(json.dumps(error_response))
    except websockets.exceptions.ConnectionClosed:
        pass
    finally:
        connected_clients.discard(websocket)
        logger.info(f"Client disconnected. Total clients: {len(connected_clients)}")

async def handle_message(data: dict) -> dict:
    """Handle incoming WebSocket messages"""
    action = data.get("action")
    
    if action == "create_user":
        username = data.get("username")
        if not username:
            return {"error": "Username required"}
        
        user_id = str(uuid.uuid4())
        users[user_id] = {
            "id": user_id,
            "username": username,
            "balance": INITIAL_BALANCE,
            "created_at": datetime.now().isoformat()
        }
        
        return {"success": True, "user": users[user_id]}
    
    elif action == "get_user":
        user_id = data.get("user_id")
        user = users.get(user_id)
        if not user:
            return {"error": "User not found"}
        return {"user": user}
    
    elif action == "create_contract":
        user_id = data.get("user_id")
        symbol = data.get("symbol")
        period = data.get("period")
        direction = data.get("direction")
        bet_amount = data.get("bet_amount")
        
        # Validation
        if not all([user_id, symbol, period, direction, bet_amount]):
            return {"error": "Missing required fields"}
        
        if symbol not in SUPPORTED_SYMBOLS:
            return {"error": f"Unsupported symbol: {symbol}"}
        
        if period not in PERIOD_SECONDS:
            return {"error": f"Unsupported period: {period}"}
        
        if direction not in ["up", "down"]:
            return {"error": f"Invalid direction: {direction}"}
        
        user = users.get(user_id)
        if not user:
            return {"error": "User not found"}
        
        if user["balance"] < bet_amount:
            return {"error": "Insufficient balance"}
        
        # Get current price
        current_price_data = prices.get(symbol)
        if not current_price_data:
            return {"error": f"No price data available for {symbol}"}
        
        anchor_price = float(current_price_data["p"])
        
        # 检查价格是否为0（连接失败状态）
        if anchor_price == 0:
            return {"error": f"Price service unavailable for {symbol}. Cannot create contract when price is 0."}
        
        anchor_timestamp = int(current_price_data["E"])
        expiry_timestamp = anchor_timestamp + (PERIOD_SECONDS[period] * 1000)
        
        # Create contract
        contract_id = str(uuid.uuid4())
        contract = {
            "id": contract_id,
            "user_id": user_id,
            "symbol": symbol,
            "period": period,
            "direction": direction,
            "bet_amount": bet_amount,
            "anchor_price": anchor_price,
            "anchor_timestamp": anchor_timestamp,
            "expiry_timestamp": expiry_timestamp,
            "status": "active",
            "created_at": datetime.now().isoformat()
        }
        
        contracts[contract_id] = contract
        
        # Deduct balance
        user["balance"] -= bet_amount
        
        return {"success": True, "contract": contract}
    
    elif action == "get_contracts":
        user_id = data.get("user_id")
        if user_id:
            user_contracts = [c for c in contracts.values() if c["user_id"] == user_id]
            return {"contracts": user_contracts}
        else:
            return {"contracts": list(contracts.values())}
    
    elif action == "get_prices":
        return {"prices": prices}
    
    else:
        return {"error": f"Unknown action: {action}"}

async def main():
    """Main server function"""
    print("Starting Event Contract Demo Server (Connection Failure Test)...")
    print("=" * 60)
    print("⚠️  此版本故意使用无效URL来测试连接失败情况")
    print("🔴 预期结果：价格将显示为0")
    print()
    
    # Start background tasks
    price_task = asyncio.create_task(start_price_feeds())
    contract_task = asyncio.create_task(check_expired_contracts())
    
    # Start WebSocket server
    server = await serve(handle_client, "0.0.0.0", 8767)  # 使用不同端口
    
    print("✅ Demo server started!")
    print("📡 WebSocket server: ws://localhost:8767")
    print("🎯 Supported symbols:", SUPPORTED_SYMBOLS)
    print("⏰ Supported periods:", list(PERIOD_SECONDS.keys()))
    print("\n📖 Usage:")
    print("Connect to ws://localhost:8767 and send JSON messages:")
    print('{"action": "create_user", "username": "test"}')
    print('{"action": "get_prices"}')
    print('{"action": "create_contract", "user_id": "...", "symbol": "ETHUSDT", "period": "10m", "direction": "up", "bet_amount": 100}')
    print("\nPress Ctrl+C to stop")
    
    try:
        await server.wait_closed()
    except KeyboardInterrupt:
        print("\n🛑 Shutting down server...")
        price_task.cancel()
        contract_task.cancel()
        server.close()
        await server.wait_closed()

if __name__ == "__main__":
    asyncio.run(main())
